﻿#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QThread>

extern QQueue<cv::Mat> detectFrameQueue;
extern QMutex detectMutex;

extern QQueue<cv::Mat> videoFrameQueue;
extern QMutex videoMutex;

extern QQueue<ROI_FRAME> roiFrameQueue;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    screenShotDefaultPath("./ScreenShots"),
    recordDefaultPath("./RecordVideo")
{
    ui->setupUi(this);

    qRegisterMetaType<Mat>("Mat"); /* 在Qt元对象中注册Mat类型 */
    qRegisterMetaType<ROI_FRAME>("ROI_FRAME"); /* 在Qt元对象中注册ROI_FRAME类型 */

    styleSheetInit();
    slotsInit();
    errorCodeInit();
    serialPortInit();
    playerInit();

}

MainWindow::~MainWindow()
{
    playerDeinit();
    delete ui;
}

void MainWindow::slotsInit()
{
    connect(ui->btnForward,    &QPushButton::clicked, this, &MainWindow::slotBtnForward);
    connect(ui->btnBackward,   &QPushButton::clicked, this, &MainWindow::slotBtnBackward);
    connect(ui->btnTurnLeft,   &QPushButton::clicked, this, &MainWindow::slotBtnTurnLeft);
    connect(ui->btnTurnRight,  &QPushButton::clicked, this, &MainWindow::slotBtnTurnRight);
    connect(ui->btnShiftLeft,  &QPushButton::clicked, this, &MainWindow::slotBtnShiftLeft);
    connect(ui->btnShiftRight, &QPushButton::clicked, this, &MainWindow::slotBtnShiftRight);

    connect(ui->btnShiftForward, &QPushButton::clicked, this, &MainWindow::slotBtnShiftForward);
    connect(ui->btnOrigin, &QPushButton::clicked, this, &MainWindow::slotBtnOrigin);
    connect(ui->btnPress, &QPushButton::clicked, this, &MainWindow::slotBtnPress);

    connect(ui->btnOpenVideo,  &QPushButton::clicked, this, &MainWindow::slotBtnOpenVideo);
    connect(ui->btnOpenSerial,  &QPushButton::clicked, this, &MainWindow::slotBtnOpenSerial);
    connect(ui->btnScreenShot,  &QPushButton::clicked, this, &MainWindow::slotBtnScreenShot);
    connect(ui->btnRecord,  &QPushButton::clicked, this, &MainWindow::slotBtnRecord);

    QList<QPushButton *> btnList;          /* 操作按键列表 */
    btnList = ui->groupBoxRobot->findChildren<QPushButton *>();
    foreach (QPushButton * btn, btnList) {
        btn->setAutoRepeat(true);          /* 设置重复按 */
        btn->setAutoRepeatDelay(1000);     /* 设置按键按下多长时间开启重复 */
        btn->setAutoRepeatInterval(100);   /* 设置重复间隔时间 */
    }
}

void MainWindow::styleSheetInit()
{
    QPalette palette(this->palette());
    palette.setColor(QPalette::Background, QColor(9,30,80));
    this->setAutoFillBackground(true);
    this->setPalette(palette);

    ui->Rocker->setStyleSheet("light");
/* https://www.cnblogs.com/liming19680104/p/10834523.html */
    ui->frameState->setFrameStyle(QFrame::Panel);
    ui->frameState->setFrameShadow(QFrame::Raised);
    ui->frameState->setLineWidth(5);

    QString tStyle = "QPushButton{font-size:16px;}";
    QList<QPushButton *> btnList = ui->frameState->findChildren<QPushButton*>();
    foreach (QPushButton *btn, btnList)
    {
        btn->setStyleSheet(tStyle);
    }

    tStyle = "QLabel{font-size:16px;}";
    QList<QLabel *> labList = ui->frameState->findChildren<QLabel*>();
    foreach (QLabel *lab, labList)
    {
        lab->setStyleSheet(tStyle);
    }

    ui->btnStateConnect->setIconSize(QSize(48,48));
    ui->btnStateConnect->setIcon(QIcon(":/imgs/online.png"));
    ui->btnStateConnect->setText("状态：");

    ui->btnStatePower->setIconSize(QSize(48,48));
    ui->btnStatePower->setIcon(QIcon(":/imgs/power.png"));
    ui->btnStatePower->setText(("电量："));
/* http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html */

    int val =43;
    QString vPower = QString::asprintf("%d%%", val);
    ui->labelPower->setText(vPower);
    ui->labelConnnect->setText("在线");


    ui->lcdNumber->setStyleSheet("QLCDNumber{color:#FF0000;}");
    ui->lcdNumber->setAttribute(Qt::WA_TranslucentBackground, true);
    ui->lcdNumber->setMode(QLCDNumber::Dec);
    ui->lcdNumber->setDigitCount(5);
//    ui->lcdNumber->setFrameStyle(QFrame::Panel | QFrame::Raised);
    ui->lcdNumber->setSegmentStyle(QLCDNumber::Filled);
    ui->lcdNumber->setBackgroundRole(QPalette::Background);
    ui->lcdNumber->display(202.2);

    QFile file(":/qss/style.qss");
    if(!file.open(QFile::ReadOnly)) return;
    QTextStream textStream(&file);
    QString stylesheet = textStream.readAll();
    this->setStyleSheet(stylesheet);
    file.close();
}

void MainWindow::errorCodeInit()
{
    ui->tableWidgetErrorLog->clear();
    ui->tableWidgetErrorLog->setRowCount(0);

    QHeaderView *headerViewVert = ui->tableWidgetErrorLog->verticalHeader();
    headerViewVert->setHidden(true);       /* 不显示tablewidget自带的序号 */

    QStringList headerHori;
    headerHori.append("错误码");
    headerHori.append("错误信息");
//    headerHori << "错误码" << "错误信息";

    ui->tableWidgetErrorLog->setColumnCount(headerHori.size());
    ui->tableWidgetErrorLog->setHorizontalHeaderLabels(headerHori);
    ui->tableWidgetErrorLog->horizontalHeader()->setStyleSheet("QHeaderView::section{background:#1296db;}");
    for(int i = 0; i < headerHori.count(); i++) /* 设置拉伸规则 */
    {
        if(i == 0)
        {
            ui->tableWidgetErrorLog->horizontalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents);
            continue;
        }
        ui->tableWidgetErrorLog->horizontalHeader()->setSectionResizeMode(i, QHeaderView::Stretch);
    }
    ui->tableWidgetErrorLog->setEditTriggers(QAbstractItemView::NoEditTriggers);  /* 设置不可编辑 */


    /* 初始化错误码和错误信息 */
    errorCodeDict.insert(ERROR_NO, "success");
    errorCodeDict.insert(ERROR_PyInit, "python module init failed");
    errorCodeDict.insert(ERROR_NumpyInit, "numpy module init failed");
    errorCodeDict.insert(ERROR_CapOpen, "camera connect failed ");
    errorCodeDict.insert(ERROR_ReadRtspTimeOut, "read rtsp stream time out");
    errorCodeDict.insert(ERROR_EmptyFrame, "frame is empty");
    errorCodeDict.insert(ERROR_SerialOpen, "open serial failed");
    errorCodeDict.insert(ERROR_LoadModel, "load algorithm model failed");
    errorCodeDict.insert(ERROR_RecordVideo, "failed to record video");
    errorCodeDict.insert(ERROR_CreateRecordPath, "failed to create record folder");


}

void MainWindow::appendErrorInfo(int index)
{
    QVector<int> codeList;

    for (int i = 0; i < ui->tableWidgetErrorLog->rowCount(); i++) {
        codeList.append(ui->tableWidgetErrorLog->item(i,0)->text().toInt());
    }
    if(codeList.contains(index)) return;

    QTableWidgetItem *item0 = new QTableWidgetItem();
    QString text = QString::number(index);
    item0->setText(text);
    item0->setTextAlignment(Qt::AlignVCenter|Qt::AlignHCenter);

    QTableWidgetItem *item1 = new QTableWidgetItem();
    text = errorCodeDict.value(index);
    item1->setText(text);
    item1->setTextAlignment(Qt::AlignVCenter|Qt::AlignLeft);

    int rows = ui->tableWidgetErrorLog->rowCount();
    ui->tableWidgetErrorLog->insertRow(rows);

    ui->tableWidgetErrorLog->setItem(rows, 0, item0);
    ui->tableWidgetErrorLog->setItem(rows, 1, item1); /* 在最后一行追加信息 */

}

void MainWindow::removeErrorinfo(int val)
{
    QVector<int> codeList;

    for (int i = 0; i < ui->tableWidgetErrorLog->rowCount(); i++) {
        codeList.append(ui->tableWidgetErrorLog->item(i,0)->text().toInt());
    }
//    qDebug() << codeList;
    if(!codeList.contains(val)) return;
    int index = codeList.value(val); // 获取所在行

//    qDebug() << index;
    ui->tableWidgetErrorLog->removeRow(index);

}

void MainWindow::playerInit()
{
#ifdef USING_DETECT
    /* python 算法初始化 */
    pDetecter = new DetectImage();
    connect(pDetecter, &DetectImage::sigSendErrorCode, this, &MainWindow::slotRecvErrorCode);
    pDetecter->setRun(true);
    pDetecter->start();
#endif
    pPlayer = new VideoPlayer("rtsp://192.168.144.25:8554/main.264"); /* 打开rtsp流 */
    connect(pPlayer, &VideoPlayer::sigSendErrorCode, this, &MainWindow::slotRecvErrorCode);
    pVideoTimer = new QTimer(this);
    connect(pVideoTimer, &QTimer::timeout, this, &MainWindow::slotVideoTimerOut);
}

void MainWindow::playerDeinit()
{
    if(ui->btnOpenVideo->text() == "关闭视频")
        slotBtnOpenVideo();
#ifdef USING_DETECT
    pDetecter->setRun(false);
    pDetecter->quit();
    pDetecter->wait();
    delete pDetecter;
    pDetecter = nullptr;
#endif
    pPlayer->quit();
    pPlayer->wait();
    delete pPlayer;
    pPlayer = nullptr;

}

void MainWindow::startPlayer()
{
#ifdef USING_DETECT
    pDetecter->setPause(true);
//    pDetecter->start();
#endif
    pPlayer->setRun(true);
    pPlayer->start();
    pVideoTimer->start(1);
}

void MainWindow::stopPlayer()
{
    pPlayer->setRun(false);
#ifdef USING_DETECT
    pDetecter->setPause(false);
#endif
    pVideoTimer->stop();
}

void MainWindow::serialPortInit()
{
    pSerial = new QSerialPort(this);
    pSerial->setBaudRate(QSerialPort::Baud57600);
    pSerial->setDataBits(QSerialPort::Data8);
    pSerial->setStopBits(QSerialPort::OneStop);
    pSerial->setParity(QSerialPort::NoParity);
    pSerial->setFlowControl(QSerialPort::NoFlowControl);

    connect(pSerial, &QSerialPort::readyRead, this, &MainWindow::slotSerialReadyRead);

    ui->cBSerialPort->clear();
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        ui->cBSerialPort->addItem(info.portName());
    }
}


QImage MainWindow::cvMat2QImage(const Mat &mat)
{
    if (mat.type() == CV_8UC1)                                      // 8-bits unsigned, NO. OF CHANNELS = 1
    {
        QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
        image.setColorCount(256);                                  // Set the color table (used to translate colour indexes to qRgb values)
        for (int i = 0; i < 256; i++)
            image.setColor(i, qRgb(i, i, i));

        uchar *pSrc = mat.data;                                    // Copy input Mat
        for (int row = 0; row < mat.rows; row++)
        {
            uchar *pDest = image.scanLine(row);
            memcpy(pDest, pSrc, static_cast<size_t>(mat.cols));
            pSrc += mat.step;
        }
        return image;
    }
    else if (mat.type() == CV_8UC3)               // 8-bits unsigned, NO. OF CHANNELS = 3
    {
        const uchar *pSrc = reinterpret_cast<const uchar*>(mat.data);  // Copy input Mat
        QImage image(pSrc, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_RGB888); // Create QImage with same dimensions as input Mat
        return image.rgbSwapped();
    }
    else if (mat.type() == CV_8UC4)
    {
        const uchar *pSrc = reinterpret_cast<const uchar*>(mat.data);
        QImage image(pSrc, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_ARGB32);
        return image.copy();
    }
    else
    {
        return QImage();  // Mat could not be converted to QImage
    }
}

bool MainWindow::createFolder(QString folder)
{
    if(QFile::exists(folder)) return true;
    QDir dir;
    return dir.mkdir(folder);
}

void MainWindow::showAndSaveImage(QImage img)
{
    if(!createFolder(screenShotDefaultPath))
    {
        appendErrorInfo(ERROR_CreateScreenShotPath);
        return;
    }
    removeErrorinfo(ERROR_CreateScreenShotPath);

    pLabelImage = new QLabel(this, Qt::Dialog | Qt::WindowCloseButtonHint);
    QDateTime tTime = QDateTime::currentDateTime();
    QString name = tTime.toString("yyyy-mm-dd-hh-mm-ss").append(".jpg");
    pLabelImage->setWindowTitle(name);
    pLabelImage->installEventFilter(this);  /* 安装事件过滤器 */

    pLabelImage->resize(640,360);
    pLabelImage->setPixmap(QPixmap::fromImage(img.scaled(640, 360, Qt::KeepAspectRatio)));
    pLabelImage->show();
    QString path = QString("%1/%2").arg(screenShotDefaultPath).arg(name);
    img.save(path);

}

bool MainWindow::eventFilter(QObject *obj, QEvent *e)
{
    if(obj == pLabelImage && e->type() == QEvent::Close)
    {
        pLabelImage->deleteLater();
        pLabelImage = nullptr;
    }
    return QMainWindow::eventFilter(obj, e);
}

void MainWindow::slotRecvErrorCode(int index)
{
    appendErrorInfo(index);
}

void MainWindow::slotVideoTimerOut()
{
    pVideoTimer->stop();

#ifdef USING_DETECT
    detectMutex.lock();
    bool isok = roiFrameQueue.isEmpty();
    detectMutex.unlock();
#else
    videoMutex.lock();
    bool isok = videoFrameQueue.isEmpty();
    videoMutex.unlock();
#endif
    if(!isok)
    {
#ifdef USING_DETECT
        detectMutex.lock();
        ROI_FRAME srcFrame = roiFrameQueue.dequeue();
        detectMutex.unlock();

        QImage img = cvMat2QImage(srcFrame.frame);

        if(srcFrame.exist == 1)
        {
            Point p1(srcFrame.x1,srcFrame.y1);
            Point p2(srcFrame.x2,srcFrame.y2);
            Mat roiImg0 = srcFrame.frame(Rect(p1,p2)).clone();

            QImage roiImg = cvMat2QImage(roiImg0);
            ui->labelImage->setPixmap(QPixmap::fromImage(roiImg.scaled(ui->labelImage->width(), ui->labelImage->height(),
                                                                                    Qt::KeepAspectRatio)));
        }
#else
        videoMutex.lock();
        Mat srcFrame = videoFrameQueue.dequeue();
        videoMutex.unlock();
        QImage img = cvMat2QImage(srcFrame);
#endif


        if(mScreenShotFlag)  /* 显示并保存一帧图像 */
        {
            mScreenShotFlag = false;
            showAndSaveImage(img);
        }
        ui->labelVideo->setImage(img); /* 显示视频，使用QPainter绘制 */
    }

    pVideoTimer->start(5);
}

void MainWindow::slotRecordTimerOut()
{
    if((++mRecordTime) <= 60)
    {
        ui->labelRecord->setText(QString("00:%1").arg(mRecordTime,2,10,QChar('0')));
        return;
    }
    ui->labelRecord->setText(QString("%1:%2").arg(mRecordTime/60,2,10,QChar('0')).arg(mRecordTime%60,2,10,QChar('0')));
}

void MainWindow::slotSerialReadyRead()
{

}

void MainWindow::slotBtnForward()
{
    qDebug() << __LINE__;
}

void MainWindow::slotBtnBackward()
{
    qDebug() << __LINE__;
}

void MainWindow::slotBtnTurnLeft()
{
    qDebug() << __LINE__;
}

void MainWindow::slotBtnTurnRight()
{
    qDebug() << __LINE__;
}

void MainWindow::slotBtnShiftLeft()
{
    qDebug() << __LINE__;
}

void MainWindow::slotBtnShiftRight()
{
    qDebug() << __LINE__;
}

void MainWindow::slotBtnOrigin()
{
    qDebug() << __LINE__;
}

void MainWindow::slotBtnShiftForward()
{
    qDebug() << __LINE__;
}

void MainWindow::slotBtnPress()
{
    qDebug() << __LINE__;
}

void MainWindow::slotBtnOpenVideo()
{
    if(ui->btnOpenVideo->text() == "打开视频")
    {
        this->startPlayer();
        ui->btnOpenVideo->setText("关闭视频");
        ui->btnOpenVideo->setStyleSheet("QPushButton{image:url(:/imgs/video_opened.png)}");
    }
    else
    {
        this->stopPlayer();
        ui->btnOpenVideo->setText("打开视频");
        ui->btnOpenVideo->setStyleSheet("QPushButton{image:url(:/imgs/video.png)}");
    }
}

void MainWindow::slotBtnOpenSerial()
{
    if(ui->btnOpenSerial->text() == "打开串口")
    {
        if(ui->cBSerialPort->currentText() == nullptr) return;

        pSerial->setPortName(ui->cBSerialPort->currentText());
        if(!pSerial->open(QIODevice::ReadWrite))
        {
            appendErrorInfo(ERROR_SerialOpen);
            return;
        }
        removeErrorinfo(ERROR_SerialOpen);
        ui->btnOpenSerial->setText("关闭串口");
        ui->btnOpenSerial->setStyleSheet("QPushButton{image:url(:/imgs/serial_opened.png)}");
    }
    else
    {
        if(!pSerial->isOpen()) return;
        pSerial->close();
        ui->btnOpenSerial->setText("打开串口");
        ui->btnOpenSerial->setStyleSheet("QPushButton{image:url(:/imgs/serial.png)}");
    }
}

void MainWindow::slotBtnScreenShot()
{
    mScreenShotFlag = true;
}

void MainWindow::slotBtnRecord()
{
    if(ui->btnRecord->text() == "录像")
    {
        if(!createFolder(recordDefaultPath))
        {
            appendErrorInfo(ERROR_CreateRecordPath);
            return;
        }

        QString cDateTime = QDateTime::currentDateTime().toString("yyyy-mm-dd-hh-mm-ss");
        QString recordFileName = QString("%1/%2.avi").arg(recordDefaultPath).arg(cDateTime);

        int ret = pPlayer->record(recordFileName.toStdString());
        if(ret != ERROR_NO)
        {
            appendErrorInfo(ret);
            return;
        }
        removeErrorinfo(ERROR_ThreadNoWork);
        removeErrorinfo(ERROR_CapOpen);
        removeErrorinfo(WARN_WriteIsWorking);
        removeErrorinfo(ERROR_WirteOpen);

        mRecordTime = 0;                              /* 初始化录像时间为0 */
        mRecordVideoFlag = true;                      /* 开始录像标志位为真 */

        ui->btnRecord->setText("停止录像");
        ui->btnRecord->setStyleSheet("QPushButton{image:url(:/imgs/recording.png)}");

        pRecordTimer = new QTimer(this);              /* 实例化录像定时器 */
        connect(pRecordTimer, &QTimer::timeout, this, &MainWindow::slotRecordTimerOut);
        pRecordTimer->start(1000);                    /* 开始录像 */
    }
    else
    {
        mRecordVideoFlag = false;
        ui->btnRecord->setText("录像");
        ui->btnRecord->setStyleSheet("QPushButton{image:url(:/imgs/record.png)}");
        pPlayer->disrecord();
        ui->labelRecord->clear();
        pRecordTimer->stop();
        pRecordTimer->deleteLater();
        pRecordTimer = nullptr;
    }

}



